package com.zhuhjay.common.core.socketio.handler;

import cn.hutool.core.util.StrUtil;
import com.corundumstudio.socketio.SocketIOClient;
import com.zhuhjay.common.base.util.JacksonUtils;
import com.zhuhjay.common.netty.handler.AbstractSocketHandler;
import com.zhuhjay.common.netty.handler.AuthParserHandler;
import com.zhuhjay.common.redis.util.RedisUtils;
import com.zhuhjay.common.security.LoginUserDetail;
import com.zhuhjay.common.security.context.LoginUserContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.zhuhjay.common.base.constant.CacheConstant.*;

/**
 * 对socket.io进行拦截，将用户信息放到线程上下文中
 *
 * @author ZhuHJay
 * @date 2023/7/11 11:37
 */
@Slf4j
@Component
public class AuthParserHandlerImpl implements AuthParserHandler {

    @Override
    public void handler(SocketIOClient client, Runnable someThing) {
        // 默认使用socket.io请求的token是没有问题的
        String token = client.getHandshakeData().getSingleUrlParam("token");
        String uid = RedisUtils.getCache(TOKEN_PREFIX + token);
        String infoStr = RedisUtils.getCache(USER_UNIQUE_ID_PREFIX + uid);
        if (StrUtil.isBlank(infoStr)) {
            // 如果不是关闭连接的请求, 那么就是非法请求
            if (client.isWritable()) {
                log.warn("非法请求: {}", client.getSessionId());
                client.disconnect();
                return;
            }
            /* 此时有以下情况：
             * 1. token和uid一同过期 (缓存数据没有删除干净)
             * 2. token存在, uid不存在 -> 强制下线 | 禁用用户 (缓存数据被删除干净)
             * 3. token不存在, uid不存在 -> 自动过期 (缓存数据没有删除干净)
             * - 在此借助 socket.io 来进行缓存数据的清理 (不管干不干净, 都进行清理)
             */
            // 获取所有在线uid
            Set<String> uidSet = RedisUtils.getKeys(USER_UNIQUE_ID_PREFIX + "*")
                    .stream().map(key -> key.substring(USER_UNIQUE_ID_PREFIX.length())).collect(Collectors.toSet());
            // 获取记录在线用户的key
            Set<String> keys = RedisUtils.getKeys(USER_ONLINE_PREFIX + "*");
            boolean isSearch = false;
            for (String key : keys) {
                // 过滤出用户不在线的uid
                List<String> noOnlineUid = RedisUtils.getMembers4Set(key).stream()
                        .filter(u -> !uidSet.contains(u)).collect(Collectors.toList());
                for (String u : noOnlineUid) {
                    if (AbstractSocketHandler.UID_SESSION_MAPPING.containsKey(u)) {
                        UUID uuid = AbstractSocketHandler.UID_SESSION_MAPPING.get(u);
                        if (uuid.equals(client.getSessionId())) {
                            RedisUtils.removeValue4Set(key, u);
                            LoginUserContext.setUsername(key.substring(USER_ONLINE_PREFIX.length()));
                            LoginUserContext.setUid(u);
                            isSearch = true;
                            break;
                        }
                    }
                }
                // 如果找到了, 就不用再找了
                if (isSearch) {
                    break;
                }
            }
            // 如果没有找到, 就不需要再进行下面的操作了
            if (!isSearch) {
                return;
            }
        } else {
            LoginUserDetail loginUserDetail = JacksonUtils.readValue(infoStr, LoginUserDetail.class);
            // 将用户信息放到线程上下文中
            LoginUserContext.setUserId(loginUserDetail.getId());
            LoginUserContext.setUsername(loginUserDetail.getUsername());
            LoginUserContext.setUid(String.valueOf(loginUserDetail.getUid()));
        }
        try {
            someThing.run();
        } catch (Throwable throwable) {
            log.error("消息事件token解析器异常", throwable);
        } finally {
            // 清空线程上下文
            LoginUserContext.clear();
        }
    }

}
